// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { DeviceConnectionManager, ConnectionState } from './deviceConnectionManager';
import { DeviceFileManager } from './deviceFileManager';
import { DeviceExplorerViewProvider } from './deviceExplorer';
import { ReplTerminalProvider } from './replTerminal';
import { SerialPortDiscovery } from './serialPortDiscovery';
import { StubsManager, DeviceType } from './stubsManager';

let connectionManager: DeviceConnectionManager;
let fileManager: DeviceFileManager;
let deviceExplorer: DeviceExplorerViewProvider;
let replTerminalProvider: ReplTerminalProvider;
let stubsManager: StubsManager;

export function activate(context: vscode.ExtensionContext) {
    console.log('MicroPython Extension is now active!');

    try {
        // Initialize core components
        console.log('Initializing core components...');
        connectionManager = new DeviceConnectionManager();
        fileManager = new DeviceFileManager(connectionManager);
        deviceExplorer = new DeviceExplorerViewProvider(connectionManager, fileManager);
        replTerminalProvider = new ReplTerminalProvider(connectionManager);
        stubsManager = new StubsManager(context);

        // Set initial context
        vscode.commands.executeCommand('setContext', 'micropython.deviceConnected', false);

        // Configure MicroPython stubs on activation
        console.log('Configuring stubs...');
        stubsManager.configureStubs();

        // Register commands
        console.log('Registering commands...');
        registerCommands(context);
        console.log('Commands registered successfully');
    } catch (error) {
        console.error('Error during extension activation:', error);
        vscode.window.showErrorMessage(`MicroPython Extension activation failed: ${error}`);
        return;
    }

    // Listen for connection state changes
    connectionManager.onStateChange(async (state) => {
        const isConnected = state === ConnectionState.CONNECTED;
        vscode.commands.executeCommand('setContext', 'micropython.deviceConnected', isConnected);

        if (isConnected) {
            vscode.window.showInformationMessage('✓ Connected to MicroPython device');
            deviceExplorer.refresh();

            // Detect device type and configure stubs
            await detectAndConfigureStubs();
        } else if (state === ConnectionState.DISCONNECTED) {
            vscode.window.showInformationMessage('Disconnected from MicroPython device');
        } else if (state === ConnectionState.ERROR) {
            vscode.window.showErrorMessage('Failed to connect to MicroPython device');
        }
    });

    // Auto-connect if configured
    const config = vscode.workspace.getConfiguration('micropython');
    if (config.get<boolean>('autoConnect', false)) {
        setTimeout(() => {
            vscode.commands.executeCommand('micropython.connectDevice');
        }, 1000);
    }

    context.subscriptions.push(connectionManager);
    context.subscriptions.push(deviceExplorer);
    context.subscriptions.push(replTerminalProvider);
}

function registerCommands(context: vscode.ExtensionContext) {
    // Connect Device Command
    const connectCommand = vscode.commands.registerCommand('micropython.connectDevice', async () => {
        if (connectionManager.isConnected) {
            vscode.window.showWarningMessage('Device is already connected');
            return;
        }

        const connectionType = await vscode.window.showQuickPick(
            [
                { label: 'Serial (USB)', value: 'serial' },
                { label: 'WebREPL (WiFi)', value: 'webrepl' }
            ],
            { placeHolder: 'Select connection type' }
        );

        if (!connectionType) {
            return;
        }

        try {
            if (connectionType.value === 'serial') {
                await connectSerial();
            } else {
                await connectWebREPL();
            }
        } catch (error) {
            vscode.window.showErrorMessage(`Connection failed: ${error}`);
        }
    });

    // Disconnect Device Command
    const disconnectCommand = vscode.commands.registerCommand('micropython.disconnectDevice', async () => {
        if (!connectionManager.isConnected) {
            vscode.window.showWarningMessage('No device connected');
            return;
        }

        try {
            await connectionManager.disconnect();
            vscode.window.showInformationMessage('Disconnected from device');
        } catch (error) {
            vscode.window.showErrorMessage(`Disconnect failed: ${error}`);
        }
    });

    // Upload File Command
    const uploadFileCommand = vscode.commands.registerCommand('micropython.uploadFile', async (uri?: vscode.Uri) => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }

        let localPath: string;
        if (uri) {
            localPath = uri.fsPath;
        } else {
            const activeEditor = vscode.window.activeTextEditor;
            if (!activeEditor) {
                vscode.window.showErrorMessage('No file selected');
                return;
            }
            localPath = activeEditor.document.fileName;
        }

        const fileName = path.basename(localPath);
        const remotePath = await vscode.window.showInputBox({
            prompt: 'Enter remote file path',
            value: '/' + fileName,
            placeHolder: '/main.py'
        });

        if (!remotePath) {
            return;
        }

        try {
            await vscode.window.withProgress({
                location: vscode.ProgressLocation.Notification,
                title: `💾 Uploading ${path.basename(localPath)} to device...`,
                cancellable: false
            }, async (progress) => {
                progress.report({ increment: 0, message: "Reading local file..." });

                const fileStats = fs.statSync(localPath);
                const fileSizeKB = (fileStats.size / 1024).toFixed(1);

                progress.report({ increment: 20, message: `File size: ${fileSizeKB} KB` });

                progress.report({ increment: 40, message: "Transferring to device storage..." });

                await fileManager.uploadFile(localPath, remotePath);

                progress.report({ increment: 100, message: "Upload completed!" });
            });

            const isMainPy = remotePath.endsWith('/main.py') || remotePath === 'main.py';
            const autoRunMessage = isMainPy ? '🔄 Will auto-run on device reboot!' : '';

            vscode.window.showInformationMessage(
                `✅ File saved to device storage: ${remotePath} ${autoRunMessage}`,
                'Show Files', 'REPL'
            ).then((selection) => {
                if (selection === 'Show Files') {
                    vscode.commands.executeCommand('workbench.view.explorer');
                } else if (selection === 'REPL') {
                    replTerminalProvider.showReplTerminal();
                }
            });

            deviceExplorer.refresh();
        } catch (error) {
            vscode.window.showErrorMessage(`❌ Upload failed: ${error}`);
        }
    });

    // Download File Command
    const downloadFileCommand = vscode.commands.registerCommand('micropython.downloadFile', async (file?: any) => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }

        let remotePath: string;
        if (file && file.path) {
            remotePath = file.path;
        } else {
            remotePath = await vscode.window.showInputBox({
                prompt: 'Enter remote file path to download',
                placeHolder: '/main.py'
            }) || '';
        }

        if (!remotePath) {
            return;
        }

        const fileName = path.basename(remotePath);
        const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
        let defaultPath = fileName;

        if (workspaceFolder) {
            defaultPath = path.join(workspaceFolder.uri.fsPath, fileName);
        }

        const localUri = await vscode.window.showSaveDialog({
            defaultUri: vscode.Uri.file(defaultPath),
            filters: {
                'Python Files': ['py'],
                'All Files': ['*']
            }
        });

        if (!localUri) {
            return;
        }

        try {
            await vscode.window.withProgress({
                location: vscode.ProgressLocation.Notification,
                title: 'Downloading file...',
                cancellable: false
            }, async () => {
                await fileManager.downloadFile(remotePath, localUri.fsPath);
            });

            vscode.window.showInformationMessage(`File downloaded: ${localUri.fsPath}`);

            // Open the downloaded file
            const document = await vscode.workspace.openTextDocument(localUri);
            await vscode.window.showTextDocument(document);
        } catch (error) {
            vscode.window.showErrorMessage(`Download failed: ${error}`);
        }
    });

    // Delete File Command
    const deleteFileCommand = vscode.commands.registerCommand('micropython.deleteFile', async (file?: any) => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }

        let remotePath: string;
        if (file && file.path) {
            remotePath = file.path;
        } else {
            remotePath = await vscode.window.showInputBox({
                prompt: 'Enter remote file path to delete',
                placeHolder: '/main.py'
            }) || '';
        }

        if (!remotePath) {
            return;
        }

        // Confirmation dialog
        const fileName = path.basename(remotePath);
        const confirm = await vscode.window.showWarningMessage(
            `Are you sure you want to delete "${fileName}" from the device?`,
            { modal: true },
            'Delete'
        );

        if (confirm !== 'Delete') {
            return;
        }

        try {
            await vscode.window.withProgress({
                location: vscode.ProgressLocation.Notification,
                title: `🗑️ Deleting ${fileName}...`,
                cancellable: false
            }, async () => {
                await fileManager.deleteFile(remotePath);
            });

            vscode.window.showInformationMessage(`✅ File deleted: ${fileName}`);
            deviceExplorer.refresh();
        } catch (error) {
            vscode.window.showErrorMessage(`❌ Delete failed: ${error}`);
        }
    });

    // Run File Command
    const runFileCommand = vscode.commands.registerCommand('micropython.runFile', async (uri?: vscode.Uri) => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }

        let filePath: string;
        if (uri) {
            filePath = uri.fsPath;
        } else {
            const activeEditor = vscode.window.activeTextEditor;
            if (!activeEditor) {
                vscode.window.showErrorMessage('No file selected');
                return;
            }
            filePath = activeEditor.document.fileName;
        }

        // Read file content and execute directly in RAM (no file system storage)
        const fileName = path.basename(filePath);

        try {
            await vscode.window.withProgress({
                location: vscode.ProgressLocation.Notification,
                title: `🚀 Executing ${fileName} in RAM...`,
                cancellable: false
            }, async (progress) => {
                progress.report({ increment: 0, message: "Reading local file..." });

                const fileContent = fs.readFileSync(filePath, 'utf8');

                progress.report({ increment: 30, message: "Sending code to device..." });

                await fileManager.runFileInRAM(fileContent);

                progress.report({ increment: 100, message: "Execution completed!" });
            });

            vscode.window.showInformationMessage(
                `✅ ${fileName} executed in RAM successfully! 💾 Code will be lost on reboot.`,
                'Show REPL'
            ).then((selection) => {
                if (selection === 'Show REPL') {
                    replTerminalProvider.showReplTerminal();
                }
            });

            // Auto-show REPL terminal to see output
            replTerminalProvider.showReplTerminal();
        } catch (error) {
            vscode.window.showErrorMessage(`❌ RAM Execution failed: ${error}`);
        }
    });

    // Run Selection Command
    const runSelectionCommand = vscode.commands.registerCommand('micropython.runSelection', async () => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }

        const activeEditor = vscode.window.activeTextEditor;
        if (!activeEditor) {
            vscode.window.showErrorMessage('No active editor');
            return;
        }

        const selection = activeEditor.selection;
        if (selection.isEmpty) {
            vscode.window.showWarningMessage('No code selected. Please select code to run.');
            return;
        }

        const selectedCode = activeEditor.document.getText(selection);
        const lineCount = selection.end.line - selection.start.line + 1;

        try {
            await vscode.window.withProgress({
                location: vscode.ProgressLocation.Notification,
                title: `🚀 Running ${lineCount} line(s) of code...`,
                cancellable: false
            }, async (progress) => {
                progress.report({ increment: 0, message: "Sending code to device..." });

                await fileManager.runFileInRAM(selectedCode);

                progress.report({ increment: 100, message: "Execution completed!" });
            });

            vscode.window.showInformationMessage(
                `✅ Selected code executed successfully!`,
                'Show REPL'
            ).then((selection) => {
                if (selection === 'Show REPL') {
                    replTerminalProvider.showReplTerminal();
                }
            });

            // Auto-show REPL terminal to see output
            replTerminalProvider.showReplTerminal();
        } catch (error) {
            vscode.window.showErrorMessage(`❌ Execution failed: ${error}`);
        }
    });

    // Open REPL Command
    const openReplCommand = vscode.commands.registerCommand('micropython.openRepl', () => {
        replTerminalProvider.showReplTerminal();
    });

    // Refresh Files Command
    const refreshFilesCommand = vscode.commands.registerCommand('micropython.refreshFiles', async () => {
        if (!connectionManager.isConnected) {
            const action = await vscode.window.showWarningMessage(
                'No device connected. Please connect a device first.',
                'Connect Device'
            );
            if (action === 'Connect Device') {
                await vscode.commands.executeCommand('micropython.connectDevice');
            }
            return;
        }
        deviceExplorer.refresh();
    });

    // Change Port Command
    const changePortCommand = vscode.commands.registerCommand('micropython.changePort', async () => {
        const config = vscode.workspace.getConfiguration('micropython');

        // Show port selection dialog
        const port = await SerialPortDiscovery.showPortSelectionDialog();

        if (!port) {
            return;
        }

        // Save the new port
        try {
            const target = vscode.workspace.workspaceFolders
                ? vscode.ConfigurationTarget.Workspace
                : vscode.ConfigurationTarget.Global;
            await config.update('port', port, target);

            vscode.window.showInformationMessage(`Serial port changed to ${port}. Please reconnect your device.`);

            // If currently connected, suggest reconnecting
            if (connectionManager.isConnected) {
                const action = await vscode.window.showInformationMessage(
                    'Device is currently connected. Do you want to reconnect with the new port?',
                    'Reconnect',
                    'Later'
                );

                if (action === 'Reconnect') {
                    await connectionManager.disconnect();
                    await vscode.commands.executeCommand('micropython.connectDevice');
                }
            }
        } catch (error) {
            vscode.window.showErrorMessage(`Failed to change port: ${error}`);
        }
    });

    // Open File Command (for tree view)
    const openFileCommand = vscode.commands.registerCommand('micropython.openFile', async (file: any) => {
        if (!file || !file.path) {
            return;
        }

        // Download and open the file temporarily
        const tempPath = path.join(__dirname, '..', 'temp', path.basename(file.path));

        try {
            // Ensure temp directory exists
            const tempDir = path.dirname(tempPath);
            if (!fs.existsSync(tempDir)) {
                fs.mkdirSync(tempDir, { recursive: true });
            }

            await fileManager.downloadFile(file.path, tempPath);
            const document = await vscode.workspace.openTextDocument(tempPath);
            await vscode.window.showTextDocument(document);
        } catch (error) {
            vscode.window.showErrorMessage(`Failed to open file: ${error}`);
        }
    });

    // Register all commands
    context.subscriptions.push(
        connectCommand,
        disconnectCommand,
        uploadFileCommand,
        downloadFileCommand,
        deleteFileCommand,
        runFileCommand,
        runSelectionCommand,
        openReplCommand,
        refreshFilesCommand,
        changePortCommand,
        openFileCommand
    );
}

async function connectSerial(): Promise<void> {
    const config = vscode.workspace.getConfiguration('micropython');
    let port = config.get<string>('port', '');

    if (!port) {
        // Try auto-detection first
        port = await SerialPortDiscovery.autoDetectMicroPythonDevice() || '';

        if (!port) {
            // Show port selection dialog
            port = await SerialPortDiscovery.showPortSelectionDialog() || '';
        }
    }

    if (!port) {
        throw new Error('No serial port selected');
    }

    const baudRate = config.get<number>('baudRate', 115200);
    await connectionManager.connectSerial(port, baudRate);

    // Save the port for future use (use Global if no workspace)
    try {
        const target = vscode.workspace.workspaceFolders
            ? vscode.ConfigurationTarget.Workspace
            : vscode.ConfigurationTarget.Global;
        await config.update('port', port, target);
    } catch (error) {
        // Silently ignore save errors
        console.log('Could not save port configuration:', error);
    }
}

async function connectWebREPL(): Promise<void> {
    const config = vscode.workspace.getConfiguration('micropython');
    let url = config.get<string>('webReplUrl', 'ws://192.168.4.1:8266/');

    const inputUrl = await vscode.window.showInputBox({
        prompt: 'Enter WebREPL URL',
        value: url,
        placeHolder: 'ws://192.168.4.1:8266/'
    });

    if (!inputUrl) {
        throw new Error('No WebREPL URL provided');
    }

    const password = await vscode.window.showInputBox({
        prompt: 'Enter WebREPL password (optional)',
        password: true,
        placeHolder: 'Leave empty if no password'
    });

    await connectionManager.connectWebREPL(inputUrl, password || undefined);

    // Save the URL for future use (use Global if no workspace)
    try {
        const target = vscode.workspace.workspaceFolders
            ? vscode.ConfigurationTarget.Workspace
            : vscode.ConfigurationTarget.Global;
        await config.update('webReplUrl', inputUrl, target);
    } catch (error) {
        // Silently ignore save errors
        console.log('Could not save WebREPL URL configuration:', error);
    }
}

async function detectAndConfigureStubs(): Promise<void> {
    try {
        // Wait a bit for the connection to stabilize
        await new Promise(resolve => setTimeout(resolve, 1000));

        // Get device information
        let deviceInfo = '';
        const listener = connectionManager.onDataReceived((data) => {
            deviceInfo += data;
        });

        // Send command to get system info
        await connectionManager.sendCommand('import sys; print(sys.implementation)');

        // Wait for response
        await new Promise(resolve => setTimeout(resolve, 2000));
        listener.dispose();

        // Detect device type
        const deviceType = await stubsManager.detectDeviceType(deviceInfo);

        // Configure stubs for the detected device type
        await stubsManager.configureStubs(deviceType);

        console.log(`Device type detected: ${deviceType}, stubs configured`);
    } catch (error) {
        console.error('Failed to detect device type:', error);
        // Configure with generic stubs as fallback
        await stubsManager.configureStubs(DeviceType.UNKNOWN);
    }
}

export function deactivate() {
    if (connectionManager) {
        connectionManager.dispose();
    }
    if (deviceExplorer) {
        deviceExplorer.dispose();
    }
    if (replTerminalProvider) {
        replTerminalProvider.dispose();
    }
    if (stubsManager) {
        stubsManager.removeStubs();
    }
}
